package net.xinqushi.java8stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.ToString;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 云路供应链科技有限公司 版权所有 © Copyright 2023
 *
 * @Description: StreamApi
 * @Project: JavaSource
 * @CreateDate: Created in 2023-11-21 19:38
 * @Author: <a href="xiongjie1@jtexpress.com">XiongJie</a>
 */
public class StreamApi {

    public static void main(String[] args) {
        parallel();
    }

    //1 创建
    private static void create() {
        //1
        List<Integer> list = Arrays.asList(1, 2, 8, 3, 6, 7, 8);
        Optional<Integer> first = list.stream().findFirst();
        //2
        int[] ints = {3, 2, 1};
        IntStream stream = Arrays.stream(ints);
        //3
        Stream<Integer> integerStream = Stream.of(3, 4, 5);
    }

    //2 遍历
    private static void iterate() {
        //forEach： forEach是一个终端操作方法，它接受一个Consumer函数作为参数，对流中的每个元素执行该函数。它没有返回值，因此无法将操作结果传递给后续操作。forEach会遍历整个流，对每个元素执行相同的操作
        List<Integer> list = Arrays.asList(1, 2, 8, 3, 6, 7, 8);
        list.stream().forEach(System.out::print);
        //peek： peek是一个中间操作方法，它接受一个Consumer函数作为参数，对流中的每个元素执行该函数。与forEach不同的是，peek方法会返回一个新的流，该流中的元素和原始流中的元素相同。
        List<Integer> collect1 = Stream.of(2, 3, 4, 1, 6)
                .peek(System.out::println)
                .filter(a -> a > 2)
                .collect(Collectors.toList());
        System.out.println(collect1);
    }

    //3 匹配
    private static void match() {
        List<Integer> list = Arrays.asList(1, 2, 8, 3, 6, 7, 8);
        //findFirst： findFirst 方法用于返回流中的第一个元素。它返回一个 Optional 对象，如果流为空，则返回一个空的 Optional；如果流非空，则返回流中的第一个元素的 Optional。
        Optional<Integer> first = list.stream().findFirst();
        if (first.isPresent()) {
            System.out.println(first.get());
        }
        // findAny 方法用于返回流中的任意一个元素。它返回一个 Optional 对象，如果流为空，则返回一个空的 Optional；如果流非空，则返回流中的任意一个元素的 Optional。在顺序流中，通常会返回第一个元素；而在并行流中，由于多线程的处理，可能返回不同的元素。
        Optional<Integer> any = list.parallelStream().findAny();
        if (any.isPresent()) {
            System.out.println(any.get());
        }
        any.ifPresent(System.out::println);

        boolean flag = list.stream().anyMatch(x -> x > 7);
        boolean flag2 = list.stream().mapToLong(Integer::longValue).allMatch(x -> x > 0);
        Optional<Integer> any1 = list.stream().filter(x -> x > 0).findAny();
        System.out.println(flag);
        System.out.println(flag2);
        any1.ifPresent(System.out::println);
    }

    //4 筛选
    static class Person{
        String name;
        int salary;
        int age;
        String sex;
        String city;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getSalary() {
            return salary;
        }

        public void setSalary(int salary) {
            this.salary = salary;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getSex() {
            return sex;
        }

        public void setSex(String sex) {
            this.sex = sex;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", salary=" + salary +
                    ", age=" + age +
                    ", sex='" + sex + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }

        public Person() {
        }

        public Person(String name, int salary, int age, String sex, String city) {
            this.name = name;
            this.salary = salary;
            this.age = age;
            this.sex = sex;
            this.city = city;
        }
    }
    private static void filter(){
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

    //筛选员工中工资高于8000的人，并形成新的集合
        List<String> collect = personList.stream()
                .filter(person -> person.getSalary() > 8000)
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    private static void juji(){
        //max
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> max = list.stream().max(Comparator.comparing(String::length));
        if (max.isPresent()) {
            System.out.println(max.get());
        }

        List<Integer> listNum = Arrays.asList(7, 6, 9, 4, 11, 6);
        listNum.stream().max(Comparator.comparing(Integer::intValue));
        listNum.stream().max(Comparator.naturalOrder());
        listNum.stream().max(Comparator.reverseOrder());

        //min
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        //获取员工工资最低的人
        Optional<Person> min = personList.stream().min(Comparator.comparingInt(Person::getSalary));
        if (min.isPresent()) {
            System.out.println(min);
        }

        //count
        //计算Integer集合中大于6的元素的个数。
        List<Integer> listNum2 = Arrays.asList(7, 6, 9, 4, 11, 6);
        long count = listNum2.stream().filter(i -> i > 6).count();
        System.out.println(count);
    }

    private static void flatmap(){
        //map() 方法接受一个 Function 函数作为参数，用于对 Stream 中的元素进行映射转换。对每个元素应用函数后的结果会构成一个新的 Stream
        //转大写
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        List<String> collect = list.stream().map(String::toUpperCase).collect(Collectors.toList());

        //取字母
        List<String> words = Arrays.asList("Hello", "World");
        List<String> collect1 = words.stream()
                .flatMap(word -> Stream.of(word.split("")))
                .distinct()
                .collect(Collectors.toList());

        String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};

        // flatMap() 方法类似于 map() 方法，不同之处在于它可以将每个元素映射为一个流，并将所有流连接成一个流。这主要用于解决嵌套集合的情况
        // flatMap 通常使用在List嵌套List
        String[] result = Stream.of(array)  // Stream<String[]>
                .flatMap(Stream::of)        // Stream<String>
                .toArray(String[]::new);    // [a, b, c, d, e, f]

        for (String s : result) {
            System.out.println(s);
        }
    }

    private static void reduce(){
        //reduce() 方法用于将 Stream 中的元素依次进行二元操作，得到一个最终的结果。它接受一个初始值和一个 BinaryOperator 函数作为参数
        // 求和
        List<Integer> nums = Arrays.asList(6, 9, 3, 2);
        Integer reduce = nums.stream().reduce(0, Integer::sum);
        System.out.println(reduce);
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        //工资之和
        Integer reduce1 = personList.stream().map(Person::getSalary).reduce(1, Integer::sum);
        System.out.println(reduce1);
    }

    private static void collect(){
        //1. toMap的key和value不能为null，java.lang.NullPointerException
        //2. toMap的key重复，又不写合并策略，java.lang.IllegalStateException: Duplicate key
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        List<String> collect = list.stream().map(String::toUpperCase).collect(Collectors.toList());

        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 4, 3, 6, 7);
        List<Integer> list1 = integers.stream().collect(Collectors.toList());
        Set<Integer> set = integers.stream().collect(Collectors.toSet());

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York11"));
        personList.stream().collect(Collectors.toMap(Person::getName, Function.identity(), (o, n) -> o));
        System.out.println(personList);
        final Map<String, Map<String, List<Person>>> collect1 =
                personList.stream().collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getCity)));
        System.out.println(collect1);
    }

    private static void tongji(){
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        //总人数
        Long collect = personList.stream().collect(Collectors.counting());
        //平均工资
        Double ave = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        //工资总额
        Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        Double sum2 = personList.stream().mapToDouble(Person::getSalary).sum();
        Integer sum1 = personList.stream().mapToInt(Person::getSalary).sum();
        //最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
        if (max.isPresent()) {
            max.get();
        }
    }

    private static void groupby(){
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> collect = personList.stream().collect(Collectors.partitioningBy(person -> person.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> sexMap = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> map = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getCity)));

        //partitioningBy 分2组
        //groupingBy 分多组
    }

    private static void join(){
        List<String> abc = Arrays.asList("A", "B", "C");
        String a = abc.stream().collect(Collectors.joining());
        System.out.println(a);
        String b = abc.stream().collect(Collectors.joining(", "));
        System.out.println(b);
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(", ", "姓名：", "。"));
        System.out.println(names);
    }

    private static void sorted(){
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8200, 23, "male", "New York"));
        personList.add(new Person("Jack", 7800, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        //sorted() 方法用于对 Stream 中的元素进行排序，默认是自然顺序排序。还可以提供自定义的 Comparator 参数来指定排序规则。例如：
        // 按工资升序排序（自然排序）
        personList.stream()
                .sorted(Comparator.comparing(Person::getSalary))
                .map(Person::getName)
                .collect(Collectors.toList());
        // 按年龄倒序排序
        personList.stream()
                .sorted(Comparator.comparing(Person::getAge).reversed())
                .map(Person::getName)
                .collect(Collectors.toList());
        personList.stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder()))
                .map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge))
                .map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资升序再按年龄降序
        personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge,Comparator.reverseOrder()))
                .map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资降序再按年龄降序
        personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge).reversed())
                .map(Person::getName)
                .collect(Collectors.toList());
        // 等同于 先按工资降序再按年龄降序 ， 实现Comparator
        personList.stream()
                .sorted((p1, p2) -> {
                    if (p1.getSalary() == p2.getSalary()) {
                        return p2.getAge() - p1.getAge();
                    } else {
                        return p2.getSalary() - p1.getSalary();
                    }
                }).map(Person::getName).collect(Collectors.toList());
    }

    private static void concat(){
        Integer[] integer1 = {1, 2, 3};
        Integer[] integer2 = {3, 2, 1};
        Stream.concat(Stream.of(integer1), Stream.of(integer2)).forEach(System.out::println);
    }

    private static void distinct(){
        //去重（Distinct）：distinct() 方法用于去除 Stream 中的重复元素，根据元素的 equals() 和 hashCode() 方法来判断是否重复。例如：
        Stream.of(1, 1, 2, 2, 3, 3, 4).distinct().forEach(System.out::println);
    }

    private static void limit(){
        //limit() 方法可以限制 Stream 的大小，只保留前 n 个元素
        Stream.of(1, 2, 3, 4).limit(3).forEach(System.out::println);
    }

    private static void skip(){
        //skip() 方法可以跳过 Stream 中的前 n 个元素，返回剩下的元素组成的新 Stream
        Stream.of(1, 2, 3, 4).skip(2).forEach(System.out::println);
        //假分页
        Integer page = 2;
        Integer rows = 10;
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21);
        integers.stream().skip((long)(page-1)*rows).limit(rows).forEach(System.out::print);
    }

    //并发
    private static void parallel(){
        //打印
        Stream.of(1, 2, 3, 4).parallel().forEach(System.out::println);
        //并行流适用于 无顺序要求的计算类型 求和、去重 , 比较 顺序 等就不支持了   避免共享可变值
        //并行流会将数据分成多个小块，并在多个线程上并行处理这些小块。这样可以充分利用多核处理器的优势。
        Stream.of(1, 2, 3, 4).parallel().mapToInt(x -> x).sum();

    }

}
